package com.jboltai.capability.text.extract.doc;

import cn.hutool.core.util.StrUtil;
import com.jboltai.event.text.TextEvent;
import com.jboltai.util.text.FileInfo;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.model.PicturesTable;
import org.apache.poi.hwpf.usermodel.*;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.Entry;
import org.apache.poi.poifs.filesystem.Ole10Native;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

public class DocExtractor {

    static char imgFlagChar = '\u0001'; //标记符号，表示该处可能有一个数据流，类似图片
    static char embedFlagStartChar = '\u0013';
    static char embedFlagEndChar = '\u0015';

    public static void extract(File file, Consumer<String> chunkConsumer, TextEvent textEvent) {
        try (FileInputStream fis = new FileInputStream(file)) {
            HWPFDocument document = new HWPFDocument(fis);
            Range range = document.getRange();
            //附件
            List<DirectoryNode> embedFiles = null;
            //图片
            PicturesTable picturesTable = null;

            if (textEvent.getFile2TextConverter() != null) { //定义了转换器，才需要去解析文件和图片
                embedFiles = getDocEmbeds(document);
                picturesTable = document.getPicturesTable();
            }




            for (int i = 0; i < range.numParagraphs(); i++) {
                Paragraph para = range.getParagraph(i);
                int paraCount = para.numCharacterRuns(); //总段落数

                boolean isEmbed = false;//当前处理的段落是否代表是嵌入的附件
                for (int paraIndex = 0; paraIndex < paraCount; paraIndex++) {
                    CharacterRun run = para.getCharacterRun(paraIndex);
                    String text = run.text();
                    if (text.length() == 1 && text.charAt(0) == embedFlagStartChar) {
                        //代表检测到了嵌入的附件
                        isEmbed = true;
                        if (embedFiles != null && embedFiles.isEmpty() == false) {
                            DirectoryNode embed = embedFiles.remove(0); //检测到一个就取一个
                            if (embed != null) {
                                //todo 解析的附件，如果是excel、word、ppt，暂时无法处理，对应的值是null，所以这里要跳过
                                Ole10Native ole = Ole10Native.createFromEmbeddedOleObject(embed);
                                byte[] data = ole.getDataBuffer();
                                try (ByteArrayInputStream is = new ByteArrayInputStream(data)) {
                                    String fileConvertText = textEvent.getFile2TextConverter().convert(textEvent, new FileInfo(ole.getLabel2(), is));
                                    chunkConsumer.accept((StrUtil.isBlank(fileConvertText) ? "" : fileConvertText) + "\n");
                                }
                            }
                        }

                    } else if (text.length() == 1 && text.charAt(0) == embedFlagEndChar) {
                        //嵌入的附件部分结束
                        isEmbed = false;
                    } else if (!isEmbed && text.length() == 1 && text.charAt(0) == imgFlagChar) {

                        if (picturesTable == null) continue;

                        //图片,并且不是代表附件图标的图片
                        Picture pic = picturesTable.extractPicture(run, false);
                        if (pic != null) {
                            byte[] pictureBytes = pic.getContent();
                            try (ByteArrayInputStream is = new ByteArrayInputStream(pictureBytes)) {
                                String fileConvertText = textEvent.getFile2TextConverter().convert(textEvent, new FileInfo(i + "." + pic.suggestFileExtension(), is));
                                chunkConsumer.accept((StrUtil.isBlank(fileConvertText) ? "" : fileConvertText) + "\n");
                            }

                        }
                    } else if (!isEmbed && StrUtil.isNotBlank(text)) {
                        chunkConsumer.accept(text.replaceAll("\\^\\[\\d+\\]\\^", "") + "\n");
                    }

                }
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取文档嵌入的附件
     *
     * @param document
     * @return
     * @throws Exception
     */
    private static List<DirectoryNode> getDocEmbeds(HWPFDocument document) throws Exception {
        //拿到所有的文件对象
        ObjectPoolImpl impl = (ObjectPoolImpl) document.getObjectsPool();
        DirectoryNode directory = document.getDirectory();
        if (directory == null) {
            return Collections.emptyList();
        }
        DirectoryEntry entry = directory.createDirectory("custom");
        impl.writeTo(entry);

        Iterator<Entry> entryIterator = entry.getEntries();
        if (entryIterator.hasNext() == false) {
            return Collections.emptyList();
        }
        DirectoryNode node = (DirectoryNode) entryIterator.next();
        List<DirectoryNode> files = new ArrayList<>();
        Iterator<Entry> entries = node.getEntries();
        while (entries.hasNext()) {
            Entry next = entries.next();
            next.renameTo(next.getName().replaceAll("[^0-9]", ""));
            files.add((DirectoryNode) next);
        }
        files.sort((a, b) -> {
            Long l1 = Long.parseLong(a.getName());
            Long l2 = Long.parseLong(b.getName());
            return l1 > l2 ? 1 : l1 < l2 ? -1 : 0;
        });
        for (int i = 0; i < files.size(); i++) {

            DirectoryNode file = files.get(i);

            if (file.hasEntry(Ole10Native.OLE10_NATIVE) == false) {
                //todo 如果插入的附件是excel、word、ppt, 这里无法解析，需要寻找别的解决方案
                files.set(i, null);
            }

        }
        return files;
    }
}
